package com.xcity.game.io;

import java.io.InputStream;

import naga.x.util.EnumUtils;

public class DeserializeStream extends InputStream {

	private byte[] buf;
	private int pos;
	private int size;
	
	public DeserializeStream(byte[] buf) {
		this(buf, 0, buf.length);
	}
	
	public DeserializeStream(byte[] buf, int off, int len) {
		this.buf = buf;
		this.pos = off;
		this.size = len;
	}
	
	@Override
	public int read()/* throws IOException*/ {
		checkAvailable(1);
		return buf[pos++];
	}
	
	@Override
	public int read(byte[] b)/* throws IOException*/ {
		return read(b, 0, b.length);
	}

	@Override
	public int read(byte[] b, int off, int len)/* throws IOException*/ {
		checkAvailable(len);
		System.arraycopy(buf, pos, b, off, len);
		pos += len;
		return len;
	}
	
	public boolean readBoolean() {
		return read() != 0;
	}
	
	public char readChar() {
		checkAvailable(2);
		return (char) ((buf[pos++] & 0xff << 8) + (buf[pos++] & 0xff));
	}
	
	public short readShort() {
		checkAvailable(2);
		return (short) ((buf[pos++] & 0xff << 8) + (buf[pos++] & 0xff));
	}
	
	public int readUnsignedShort() {
		checkAvailable(2);
		return (buf[pos++] & 0xff << 8) + (buf[pos++] & 0xff);
	}
	
	public int readInt() {
		checkAvailable(4);
		return (buf[pos++] & 0xff << 24) + (buf[pos++] & 0xff << 16) + (buf[pos++] & 0xff << 8) + (buf[pos++] & 0xff);
	}
	
	public long readLong() {
		checkAvailable(8);
		return ((long) (buf[pos++] & 0xff) << 56)
				+ ((long) (buf[pos++] & 0xff) << 48)
				+ ((long) (buf[pos++] & 0xff) << 40)
				+ ((long) (buf[pos++] & 0xff) << 32)
				+ ((long) (buf[pos++] & 0xff) << 24)
				+ ((long) (buf[pos++] & 0xff) << 16)
				+ ((long) (buf[pos++] & 0xff) << 8)
				+ ((long) (buf[pos++] & 0xff) << 0);
	}
	
	public float readFloat() {
		return Float.intBitsToFloat(readInt());
	}
	
	public double readDouble() {
		return Double.longBitsToDouble(readLong());
	}
	
	public String readUTF() {
		int utflen = readUnsignedShort();
        byte[] bytearr = new byte[utflen];
        char[] chararr = new char[utflen];

        int c, char2, char3;
        int count = 0;
        int chararr_count=0;

        read(bytearr, 0, utflen);

        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            if (c > 127) break;
            count++;
            chararr[chararr_count++]=(char)c;
        }

        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            switch (c >> 4) {
                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                    /* 0xxxxxxx*/
                    count++;
                    chararr[chararr_count++]=(char)c;
                    break;
                case 12: case 13:
                    /* 110x xxxx   10xx xxxx*/
                    count += 2;
                    if (count > utflen)
                        throw new IllegalStateException(
                            "malformed input: partial character at end");
                    char2 = (int) bytearr[count-1];
                    if ((char2 & 0xC0) != 0x80)
                        throw new IllegalStateException(
                            "malformed input around byte " + count);
                    chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
                                                    (char2 & 0x3F));
                    break;
                case 14:
                    /* 1110 xxxx  10xx xxxx  10xx xxxx */
                    count += 3;
                    if (count > utflen)
                        throw new IllegalStateException(
                            "malformed input: partial character at end");
                    char2 = (int) bytearr[count-2];
                    char3 = (int) bytearr[count-1];
                    if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
                        throw new IllegalStateException(
                            "malformed input around byte " + (count-1));
                    chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
                                                    ((char2 & 0x3F) << 6)  |
                                                    ((char3 & 0x3F) << 0));
                    break;
                default:
                    /* 10xx xxxx,  1111 xxxx */
                    throw new IllegalStateException(
                        "malformed input around byte " + count);
            }
        }
        // The number of chars produced may be less than utflen
        return new String(chararr, 0, chararr_count);
	}
	
	public <E extends Enum<E>> E readEnum(Class<E> enumType) {
		int ordinal = readInt();
		return EnumUtils.forOrdinal(ordinal, enumType);
	}

	@Override
	public int available()/* throws IOException*/ {
		return size - pos;
	}

	@Override
	public void reset()/* throws IOException*/ {
		pos = 0;
	}

	@Override
	public void close()/* throws IOException*/ {
	}

	private void checkAvailable(int len) {
		if (pos + len > size) {
			throw new IndexOutOfBoundsException("pos=" + pos + ",size=" + size + ". len=" + len);
		}
	}

}
